package cn.tx.redislock.mylock;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author jizenan
 * @date 2025/6/9
 * Description:自研redis分布式锁
 */
//@Component 引入DistributedLockFactory工厂模式，从工程获得即可
public class RedisDistributedLock implements Lock {

    private StringRedisTemplate stringRedisTemplate;

    private String lockName;//KEY[1]
    private String uuidValue;//ARGV[1]
    private long expireTime;//ARGV[2]

    //public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName) {
    //    this.stringRedisTemplate = stringRedisTemplate;
    //    this.lockName = lockName;
    //    this.uuidValue = IdUtil.simpleUUID()+":"+Thread.currentThread().getId();
    //    this.expireTime = 25L;
    //}

    public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName,String uuid) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.lockName = lockName;
        this.uuidValue = uuid + ":" +Thread.currentThread().getId();
        this.expireTime = 30L;
    }

    @Override
    public void lock() {
        tryLock();
    }

    @Override
    public boolean tryLock() {
        try {tryLock(-1L,TimeUnit.SECONDS);} catch (InterruptedException e) {e.printStackTrace();}
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        if(time == -1L){
            String script =
                    "if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then " +
                            "redis.call('hincrby',KEYS[1],ARGV[1],1) " +
                            "redis.call('expire',KEYS[1],ARGV[2]) " +
                            "return 1 " +
                    "else " +
                            "return 0 " +
                    "end";
            System.out.println("加锁lockName："+lockName+"\t"+"uuidValue:"+uuidValue);
            while (!stringRedisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class), Arrays.asList(lockName),
                    uuidValue,String.valueOf(expireTime))){
                //暂停60毫秒
                try { TimeUnit.MILLISECONDS.sleep(60);} catch (InterruptedException e) {e.printStackTrace();}
            }
            //新建后台扫描程序，来检测对应key的ttl，是否到规定时间的1/2 1/3
            resetExpire();
            return true;
        }
        return false;
    }

    @Override
    public void unlock() {
        String script =
                "if redis.call('hexists',KEYS[1],ARGV[1]) == 0 then " +
                        "return nil " +
                "elseif redis.call('hincrby',KEYS[1],ARGV[1],-1) == 0 then " +
                        "return redis.call('del',KEYS[1]) " +
                "else " +
                        "return 0 " +
               "end";
        System.out.println("解锁lockName："+lockName+"\t"+"uuidValue:"+uuidValue);
        Long flag = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName),
                uuidValue, String.valueOf(expireTime));
        if(null == flag){
            throw new RuntimeException("this lock doesn't exits.....");

        }
    }

    private void resetExpire() {
        String script = "if redis.call('hexists',KEYS[1],ARGV[1]) == 1 then " +
                                "return redis.call('expire',KEYS[1],ARGV[2]) " +
                        "else " +
                                "return 0 " +
                        "end";
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if(stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName),
                        uuidValue, String.valueOf(expireTime))){
                    resetExpire();
                }
            }
        },(this.expireTime * 1000)/3);
    }



    //====暂时用不到，不重写
    //====中断
    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
